package org.micode.libs.webflux.base;

import lombok.extern.slf4j.Slf4j;
import org.micode.common.base.BaseController;
import org.micode.common.config.ApplicationConfig;
import org.micode.common.domain.ApiResult;
import org.micode.common.domain.DataPage;
import org.micode.common.domain.base.BaseEntity;
import org.micode.common.domain.base.BaseQuery;
import org.micode.common.domain.base.IUserCache;
import org.micode.common.domain.base.entity.BaseUpdEntity;
import org.micode.common.domain.vo.EditObjVO;
import org.micode.common.enums.ResultEnum;
import org.micode.common.utils.StringExtUtils;
import org.micode.libs.webflux.WebExchangeHelper;
import org.springframework.web.server.ServerWebExchange;
import reactor.core.publisher.Mono;

import java.util.List;
import java.util.function.Function;
import java.util.function.Supplier;

@Slf4j
public abstract class BaseWebFluxController
        extends BaseController<ServerWebExchange> {

    @Override
    protected String getToken(ServerWebExchange exchange) {
        return WebExchangeHelper.getToken(exchange);
    }

    @Override
    protected String getLoginClientApp(ServerWebExchange exchange) {
        String ret = exchange.getAttribute(WebFluxConsts.REQUEST_HEADER_SERVICE_NAME_KEY);
        if (StringExtUtils.isAnyEmpty(ret)) {
            return ApplicationConfig.SERVICE_NAME;
        } else {
            return ret;
        }
    }

    @Override
    protected String getLoginClientIp(ServerWebExchange exchange) {
        return WebExchangeHelper.getRemoteIp(exchange);
    }

    @Override
    protected IUserCache currentLoginUser(ServerWebExchange exchange) {
        return getCurrentUserCache(exchange);
    }

    @Override
    protected Long currentUserId(ServerWebExchange exchange) {
        return currentUserIdFromHeader(exchange);
    }

    @Override
    protected IUserCache getCurrentUserCache(ServerWebExchange exchange) {
        return exchange.getAttribute(WebFluxConsts.USER_ID_KEY);
    }

    @Override
    protected Long currentUserIdFromHeader(ServerWebExchange exchange) {
        IUserCache user = exchange.getAttribute(WebFluxConsts.USER_ID_KEY);
        return null != user ? user.getUserId() : null;
    }

    protected <DTO extends BaseEntity, QRY extends BaseQuery> Mono<ApiResult<DataPage<DTO>>> listForQuery(
            Function<QRY, DataPage<DTO>> listFunc, QRY dataQuery, ServerWebExchange exchange) {
        IUserCache user = currentLoginUser(exchange);
        copyLoginUserFields(user, dataQuery.getExample());
        return invokeMethod(listFunc, dataQuery);
    }

    protected <DTO extends BaseUpdEntity> Mono<ApiResult<DTO>> insertTheDTO(
            Function<DTO, DTO> saveFunc, DTO theDTO, ServerWebExchange exchange) {
        fillLoginFields(exchange, theDTO);
//        theDTO.setUpdatedFields(theDTO.getUpdUser());
        return invokeMethod(saveFunc, theDTO);
    }

    protected <K> void fillLoginFields(ServerWebExchange exchange, K theObj) {
        IUserCache user = currentLoginUser(exchange);
        copyLoginUserFields(user, theObj);
        fillUpdFields(theObj, user.getUserId(), user.getLoginApp(), user.getLoginIp());
    }


    protected <DTO extends BaseEntity> Mono<ApiResult<Integer>> updateTheDTO(
            Function<DTO, Integer> saveFunc, DTO theDTO, ServerWebExchange exchange) {
        fillLoginFields(exchange, theDTO);
        return invokeMethod(saveFunc, theDTO);
    }

    protected Mono<ApiResult<Integer>> deleteForIds(
            Function<EditObjVO<List<Long>>, Integer> deleteFunc, List<Long> delIds,
            ServerWebExchange exchange) {
        IUserCache currentUser = currentLoginUser(exchange);
        EditObjVO<List<Long>> editObjVO =
                new EditObjVO<>(delIds,
                        currentUser.getUserId(),
                        getLoginClientApp(exchange),
                        getLoginClientIp(exchange));
        return invokeMethod(deleteFunc, editObjVO);
    }

    protected <OUT_T> Mono<ApiResult<OUT_T>> invokeMethod(Supplier<OUT_T> function) {
        return Mono.just(doInvokeMethod(function, ResultEnum.SUCCESS, null));
    }

    protected <OUT_T> Mono<ApiResult<OUT_T>> invokeMethod(Supplier<OUT_T> function,
                                                          ResultEnum nullResult,
                                                          String exceptionMsg) {
        return Mono.just(doInvokeMethod(function, nullResult, exceptionMsg));
    }

    protected <IN_T, OUT_T> Mono<ApiResult<OUT_T>> invokeMethod(Function<IN_T, OUT_T> function,
                                                                IN_T inputArg) {
        return Mono.just(doInvokeMethod(function, inputArg, ResultEnum.SUCCESS, null));
    }

    protected <IN_T, OUT_T> Mono<ApiResult<OUT_T>> invokeMethod(Function<IN_T, OUT_T> function,
                                                                IN_T inputArg,
                                                                ResultEnum nullResult,
                                                                String exceptionMsg) {
        return Mono.just(doInvokeMethod(function, inputArg, nullResult, exceptionMsg));
    }

    protected <T> Mono<ApiResult<T>> invokeFail(String argsMsg, Exception e) {
        return Mono.just(doInvokeFail(argsMsg, e));
    }

    protected <T> Mono<ApiResult<T>> returnFail(Integer resultCode, String errorMsg) {
        return Mono.just(doReturnFail(resultCode, errorMsg));
    }

    protected <T> Mono<ApiResult<T>> returnFail(String errorMsg) {
        return Mono.just(doReturnFail(errorMsg));
    }

    protected <T> Mono<ApiResult<T>> returnFailToken() {
        return Mono.just(doReturnFailToken());
    }

    protected <T> Mono<ApiResult<T>> returnSuccess(T ret) {
        return Mono.just(doReturnSuccess(ret));
    }

}
